package model;

import java.util.ArrayList;
import java.util.Iterator;

import colors.Color;

import utils.Pair;
import utils.Point3D;
import utils.TimeIntervalle;
import utils.Vector3D;

import mathematics.geometry.Geometry;
import matrix.GraphNode;
import matrix.GraphNodeType;
import matrix.GraphDoor;
import matrix.NodeLinkMatrix;
import model.path.DijkstraResult;
import model.trajectory.TargetModel;

public class RoomModel extends BaseHybridModel
{
	private Geometry geometry;
	private RoomType type;
	private Color color;
	
	private String texturePath;
	private boolean hasTexture;
	
	private ArrayList<Pair<TimeIntervalle, ArrayList<TargetModel>>> targetsByIntervalle;
	private NodeLinkMatrix matrix;
	private Point3D center;

	public RoomModel(int ID, String name, Geometry shape, RoomType type)
	{
		super(ID, name);
		this.setType(type);
		this.setHasTexture(false);
		this.setGeometricShape(shape);	
		this.setCenter(this.getGeometry().getCentroid());
		this.setTargetsByIntervalle(new ArrayList<Pair<TimeIntervalle, ArrayList<TargetModel>>> ());
	}

	public void addTarget(TimeIntervalle intervalle, TargetModel target)
	{
		for(Iterator<Pair<TimeIntervalle, ArrayList<TargetModel>>> it = this.getTargetsByIntervalle().iterator(); it.hasNext();)
		{
			Pair<TimeIntervalle, ArrayList<TargetModel>> pair = it.next();
			if(pair.key.equals(intervalle))
			{
				int index = 0;
				for(Iterator<TargetModel> itTarget = pair.value.iterator(); itTarget.hasNext();)
				{
					TargetModel currentTarget = itTarget.next();
					if(currentTarget.getID() <= target.getID())
					{
						index++;
					}
				}
				pair.value.add(index, target);
			}
		}
	}
	
	@Override
	public String toString()
	{
		return "Room: " + getID();
	}

	public ArrayList<TargetModel> targetsByTime(long time) 
	{
		ArrayList<TargetModel> result = new ArrayList<TargetModel>(); 
		for(Iterator<Pair<TimeIntervalle, ArrayList<TargetModel>>> it = this.getTargetsByIntervalle().iterator(); it.hasNext();)
		{
			Pair<TimeIntervalle, ArrayList<TargetModel>> pair = it.next();
			if(pair.key.include(time))
			{
				return pair.value;
			}
		}
		return result;
	}

	public ArrayList<TargetModel> targetsByIntervalle(TimeIntervalle intervalle) 
	{
		ArrayList<TargetModel> result = new ArrayList<TargetModel>(); 
		for(Iterator<Pair<TimeIntervalle, ArrayList<TargetModel>>> it = this.getTargetsByIntervalle().iterator(); it.hasNext();)
		{
			Pair<TimeIntervalle, ArrayList<TargetModel>> pair = it.next();
			if(intervalle.contains(pair.key))
			{
				result.addAll(pair.value);
			}
		}
		return result;
	}

	public int getTargetIndex(TimeIntervalle intervalle, TargetModel target)
	{
		ArrayList<TargetModel> targets = targetsByIntervalle(intervalle);
		int result = 0;
		for(int i = 0; i < targets.size(); i++)
		{
			if(targets.get(i) == target)
			{
				return targets.size() - i;
			}
		}
		return result;
	}

	public int getTargetSpecificIndex(TimeIntervalle totalTimeIntervalle, TimeIntervalle intervalle, TargetModel target)
	{
		ArrayList<TargetModel> targets = targetsByIntervalle(totalTimeIntervalle);
		int result = 1;
		int currentSpecificIndex = 0;
		int specificIndex = this.getTargetSpecificIndex(intervalle, target);
		for(int i = 0; i < targets.size(); i++)
		{
			if(targets.get(i) == target)
			{
				if(currentSpecificIndex == specificIndex)
				{
						return result + i;
					}
					else 
					{
						currentSpecificIndex++;
					}
				}
			}
		return result;
	}
	
	public int getTargetSpecificIndex(TimeIntervalle timeIntervalle, TargetModel target)
	{
		int result = 0;
		
		for(Iterator<Pair<TimeIntervalle, ArrayList<TargetModel>>> it = this.getTargetsByIntervalle().iterator(); it.hasNext();)
		{
			Pair<TimeIntervalle, ArrayList<TargetModel>> pair = it.next();
			if(pair.value.contains(target))
			{
				if(timeIntervalle.contains(pair.key))
				{
					return result;
				}
				result++;
			}
		}	
		return result;
	}

	public GraphNode getInnerGraphDoor(DoorModel door) 
	{
		for(Iterator<GraphNode> it = this.getMatrix().getKeys().iterator(); it.hasNext();)
		{
			GraphNode currentNode = it.next();
			if(currentNode.getType() == GraphNodeType.Door && ((GraphDoor)currentNode).getDoorModel() == door)
			{
				return currentNode;
			}
		}
		return null;
	}

	public GraphNode getRandomInterestPoint() 
	{
		GraphNode node =  (GraphNode) this.getMatrix().getKeys().get((int) (Math.random() * (this.getMatrix().getKeys().size() )) );	
		if(this.hasInterestPoint())
		{
			while(node.getType() != GraphNodeType.InterestPoint)
			{
				node =  (GraphNode) this.getMatrix().getKeys().get((int) (Math.random() * (this.getMatrix().getKeys().size() )) );	
			}
		}
		else
		{
			while(node.getType() != GraphNodeType.Door)
			{
				node =  (GraphNode) this.getMatrix().getKeys().get((int) (Math.random() * (this.getMatrix().getKeys().size() )) );	
			}
			
		}
		return node;
	}
	
	private boolean hasInterestPoint() {
		for(Iterator<GraphNode> it = this.getMatrix().getKeys().iterator(); it.hasNext();)
		{
			if( it.next().getType() == GraphNodeType.InterestPoint)
			{
				return true;
			}
		}
		return false;
	}

	public double getLength(GraphNode beginNode, GraphNode endNode)
	{
		DijkstraResult result = this.getMatrix().getShortestPath(beginNode, endNode);
		return result.getLength();
	}

	public int getInterestPointCount() 
	{
		int count = 0;
		for(Iterator<GraphNode> it = this.getMatrix().getKeys().iterator(); it.hasNext();)
		{
			if( it.next().getType() == GraphNodeType.InterestPoint)
			{
				count++;
			}
		}
		return count;
	}

	public Point3D getCenter() {
		return center;
	}

	public void setCenter(Point3D center) {
		this.center = center;
	}
	
	@Override
	public void translate(Vector3D translationVector)
	{
		super.translate(translationVector);
		this.getGeometry().setPosition ( Point3D.sum(this.getGeometry().getPosition(), translationVector ));
	}
	
	public Point3D computedPosition()
	{
		return new Point3D(this.getPosition().x() + this.getCenter().x(), this.getPosition().y() + this.getCenter().y());
	}
	
	public ArrayList<TargetModel> targetsInsideRoom(long beginTime, long endTime)
	{
		TimeIntervalle intervalle = new TimeIntervalle(beginTime, endTime);
		
		ArrayList<TargetModel> result = new ArrayList<TargetModel>();
		for(Iterator<Pair<TimeIntervalle, ArrayList<TargetModel>>> it = getTargetsByIntervalle().iterator(); it.hasNext();)
		{
			Pair<TimeIntervalle, ArrayList<TargetModel>> pair = it.next();
			if( intervalle.contains(pair.key) || pair.key.include(beginTime) || pair.key.include(endTime))
			{
				for(Iterator<TargetModel> itTarget = pair.value.iterator(); itTarget.hasNext();)
				{
					TargetModel target = itTarget.next();
					if(!result.contains(target))
					{
						result.add(target);
					}
				}
			}
		}
		return result;
	}

	public String getTexturePath() {
		return texturePath;
	}

	public void setTexturePath(String texturePath) {
		this.texturePath = texturePath;
		if(texturePath != null && texturePath != "")
		{
			this.setHasTexture(true);
		}
	}

	public boolean hasTexture() {
		return hasTexture;
	}

	public void setHasTexture(boolean hasTexture) {
		this.hasTexture = hasTexture;
	}

	public ArrayList<Pair<TimeIntervalle, ArrayList<TargetModel>>> getTargetsByIntervalle() {
		return targetsByIntervalle;
	}

	public void setTargetsByIntervalle(ArrayList<Pair<TimeIntervalle, ArrayList<TargetModel>>> targetsByIntervalle) {
		this.targetsByIntervalle = targetsByIntervalle;
	}

	public boolean isCorridor() {
		return this.getType() == RoomType.Corridor;
	}

	public RoomType getType() {
		return type;
	}

	public void setType(RoomType type) {
		this.type = type;
	}

	public NodeLinkMatrix getMatrix() {
		return matrix;
	}

	public void setMatrix(NodeLinkMatrix matrix) {
		this.matrix = matrix;
	}

	public Geometry getGeometry() {
		return geometry;
	}

	public void setGeometricShape(Geometry geometricShape) {
		this.geometry = geometricShape;
	}

	public Color getColor() {
		return color;
	}

	public void setColor(Color color) {
		this.color = color;
	}
}
